package com.example.multithread.controller;

import com.example.multithread.chapter01.CallableExample;
import com.example.multithread.chapter01.ThreadExample;
import com.example.multithread.chapter01.RunnableExample;
import com.example.multithread.chapter02.MyThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;

/**
 * @ClassName TestController
 * @Description TODO
 * @Author leejiliang
 * @DateTime 2020/4/27 3:02 PM
 * @Version 1.0
 */
@RestController
public class TestSimpleMultiThreadController {


	private Logger logger = LoggerFactory.getLogger(TestSimpleMultiThreadController.class);
	@GetMapping("thread/test")
	public long calculator() throws InterruptedException {

		List<Long> result = new CopyOnWriteArrayList<>();
		//Extend Thread
		StopWatch clock = new StopWatch();
		clock.start(Thread.currentThread().getName());
		ThreadExample e1 = new ThreadExample(1L, 33000L, result);
		ThreadExample e2 = new ThreadExample(33001L, 67000L, result);
		ThreadExample e3 = new ThreadExample(67001L, 100000L, result);
		ThreadExample e4 = new ThreadExample(100001L, 200000L, result);
		ThreadExample e5 = new ThreadExample(200001L, 300000L, result);
		ThreadExample e6 = new ThreadExample(300001L, 400000L, result);
		ThreadExample e7 = new ThreadExample(400001L, 500000L, result);
		ThreadExample e8 = new ThreadExample(500001L, 600000L, result);
		ThreadExample e9 = new ThreadExample(600001L, 1000000L, result);
		e1.start();
		e1.start();
		e2.start();
		e3.start();
		e4.start();
		e5.start();
		e6.start();
		e7.start();
		e8.start();
		e9.start();
		e2.join();
		e3.join();
		e4.join();
		e5.join();
		e6.join();
		e7.join();
		e8.join();
		e9.join();
		LoggerFactory.getLogger(TestSimpleMultiThreadController.class).info("现在获取到了:{} 个值.", e1.result.size());
		Optional<Long> reduce = e1.result.stream().reduce(Long::sum);
		clock.stop();
//		System.out.println(clock.prettyPrint());
		return reduce.get();
	}

	@GetMapping("/runnable")
	public void testRunnable() throws InterruptedException {

		RunnableExample runnableExample = new RunnableExample(new AtomicInteger(0));
		Thread t1 = new Thread(runnableExample);
		t1.start();
		Thread.sleep(1000);
		logger.info("t1 sleep中, 状态是: {}", t1.getState());
		Thread t2 = new Thread(runnableExample);
		t2.start();
		t1.join();
		t2.join();
		logger.info(Thread.currentThread().getName());
	}

	@GetMapping("/callable")
	public void testCallable() throws Exception {
		CallableExample callableExample = new CallableExample();
		var futureTask = new FutureTask(callableExample);
		new Thread(futureTask).start();
		futureTask.get();
		logger.info(Thread.currentThread().getName());
	}

	@GetMapping("/slow")
	public void testSingleThread() {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start("任务开始了.");
		for (int i = 0; i < 10; i++) {
			RunnableExample runnableExample = new RunnableExample(new AtomicInteger(0));
			runnableExample.run();
		}
		stopWatch.stop();
		logger.info(stopWatch.getTotalTimeSeconds() + "");
		logger.info(stopWatch.prettyPrint());
	}
	//试试线程池

	private ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
	private ExecutorService fixedThreadPoolWithFactory = Executors.newFixedThreadPool(10, new MyThreadFactory("我勒个去"));


	@GetMapping("/thread-pool")
	public void testThreadPool() {
		ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);//因为在后面为了阻塞主线程, 线程池会被shutdown, 所以每次使用都要重新创建线程池
		StopWatch stopWatch = new StopWatch();
		stopWatch.start("任务开始了.");
		IntStream.rangeClosed(1,10).forEach((i)->{
			RunnableExample runnableExample = new RunnableExample(new AtomicInteger(0));
			fixedThreadPool.execute(runnableExample);
		});
		//阻塞的方式真恶心
		fixedThreadPool.shutdown();//这里shutdown了, 就要求每次在用的时候就重新new ThreadPool.
		while (true) {
			if (fixedThreadPool.isTerminated()) {
				stopWatch.stop();
				logger.info(stopWatch.getTotalTimeMillis() + "");
				logger.info(stopWatch.prettyPrint());
				break;
			}
		}
	}

	@GetMapping("/thread-pool/2")
	public void testThreadPool2() {
		List<Future> list = new ArrayList<>();
		StopWatch stopWatch = new StopWatch();
		stopWatch.start("任务开始了.");
		IntStream.rangeClosed(1,10).forEach((i)->{
			Callable callable = new CallableExample();
			FutureTask futureTask = new FutureTask(callable);
			fixedThreadPoolWithFactory.execute(futureTask);
			list.add(futureTask);
		});
		//阻塞的方式会不会好点?总不至于每次shutdown掉线程池
		list.forEach(i-> {
			try {
				logger.info(i.get().toString());
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		});
		stopWatch.stop();
		logger.info(stopWatch.getTotalTimeMillis()+"");
		logger.info(stopWatch.prettyPrint());
	}

	public void testThreadPool3() {
		new ThreadPoolExecutor(1, 	//核心线程数量, CPU密集型任务时:Runtime.availableProcessors+1,   CPU数量 * CPU利用率 * (1 + 线程等待时间/线程CPU时间)
				10,	//最大线程数量
				60,	//超过核心线程数以外的空闲线程最长回收时间
				TimeUnit.SECONDS,	//keepAliveTime 单位
				null,	//存放待执行任务的队列
				new MyThreadFactory("122"),	//创建线程的工厂
				null);	//拒绝任务的策略
	}
//	ReentrantLock
	ThreadLocal<Integer> local = new ThreadLocal<>();
	@GetMapping("/block-queue")
	public void testBlockingQueue() throws InterruptedException {
		AtomicInteger count = new AtomicInteger(0);
		BlockingQueue<Runnable> queue1 = new LinkedTransferQueue<>();//无界队列, 小心OOM
//		BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(39);	//有界队列
		BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(10);	//无界队列
		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(12, 20, 6000, TimeUnit.MILLISECONDS, queue, new ThreadPoolExecutor.CallerRunsPolicy());
		StopWatch clock = new StopWatch();
		clock.start("计算1+1等于几");
		int i = 0;
		while (i < 60) {
			threadPoolExecutor.execute(new RunnableExample(count));
			i++;
		}
		threadPoolExecutor.shutdown();
		while (true) {
			logger.info("阻塞队列中现在有{} 个线程.", queue.size());
			Thread.sleep(1000);
			if (threadPoolExecutor.isTerminated()) {
				clock.stop();
				logger.info("耗时:{}毫秒, 共计执行了{} 次运算,  其他信息: {}", clock.getTotalTimeMillis(), count.get(), clock.prettyPrint());
				break;
			}
		}
	}



}
